﻿
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Diagnostics;

namespace ImageCompressor
{
    /// <summary>
    /// 单张JPG的压缩运算
    /// </summary>
    class JPGCompressor
    {
        int compressLevel;
        int MaxWidth;
        int MaxHeight;
        long Quality;

        ImageCodecInfo jpegEncoder;
        EncoderParameters jpeParameters;

        public JPGCompressor(int compressLevel, int maxWidth,int maxHeight)
        {
            CompressLevel= compressLevel;
            MaxWidth = maxWidth; 
            MaxHeight = maxHeight; 

        }
        /// <summary>
        /// 设定压缩质量
        /// </summary>
        public int CompressLevel
        {
            get { return compressLevel; }
            private set
            {
                compressLevel = value;
                Quality = compressLevel * 5 + 50; //(50-100)
                if (!SetJpegCompressor()) throw new Exception("Cannot Create JPG Comporessor");
            }
        }

        public static int CompressionLevelToMaxWidth(int level)
        {
            return Math.Max((int)(1920 * (level * 0.2)), 480);//480-3840
        }
        public static int CompressionLevelToMaxHeight(int level)
        {
            return Math.Max((int)(1080 * (level * 0.2)), 360);//360-2160
        }

        


        #region CompressBytes

        /// <summary>
        /// 储存Bitmap到JPG文件。
        /// 之后释放 writeBitmap
        /// </summary>
        public bool SaveCompressBytes(byte[] saveBytes, FileInfo saveFile, out string errorMessage)
        {
            errorMessage = null;
            if (saveFile.Exists)
            {
                if (saveFile.IsReadOnly)
                {
                    errorMessage = Localize.Get("Save file is ReadOnly. ");
                    return false;
                }
            }
            else
            {
                DirectoryInfo directory = saveFile.Directory;
                if (!directory.Exists)
                {
                    try
                    {
                        directory.Create();
                    }
                    catch
                    {
                        errorMessage = Localize.Get("Cannot create save folder. ");
                        return false;
                    }
                }
            }
            try
            {
                File.WriteAllBytes(saveFile.FullName, saveBytes);
                return true;

            }
            catch (Exception ex)
            {
                if (ex is IOException)
                {
                    errorMessage = Localize.Get("Save file is occupied. ");
                    return false;
                }
                errorMessage = Localize.Get("Save file error at ")+$"{ex.Message}";
                return false;
            }
        }

        /// <summary>
        /// 打开一个Image文件,返回Bytes
        /// </summary>
        /// <returns></returns>
        public byte[] ReadFileToBytes(FileInfo readFile, out string errorMessage)
        {
            errorMessage = null;
            if (!readFile.Exists)
            {
                errorMessage = Localize.Get("Read file not exist. ");
                return null;
            }
            if(readFile.Length > 1024 * 1024 * 256)
            {
                errorMessage = Localize.Get("Read image larger than 256 MB");
                return null;
            }
            try
            {
                return File.ReadAllBytes(readFile.FullName);
            }
            catch (Exception ex)
            {
                errorMessage = Localize.Get("Read file error at ") + $"{ex.Message}";
                return null;
            }
        } 
        
        
        public byte[] CompressBytes(byte[] readBytes, out string errorMessage) 
        {
            MemoryStream memoryStream = null;
            MemoryStream saveMemoryStream = null;
            Bitmap rawBit = null;
            Graphics graphics = null;
            Bitmap newBit = null;

            errorMessage = null;
            try
            {
                memoryStream = new MemoryStream(readBytes, false);
                saveMemoryStream = new MemoryStream();
                rawBit = new Bitmap(memoryStream);
                memoryStream.Close();
                memoryStream.Dispose();
                double percentW = (double)MaxWidth / rawBit.Width;
                double percentH = (double)MaxHeight / rawBit.Height;
                double percent = Math.Min(percentW, percentH);
                percent = Math.Min(1, percent);
                if (percent < 1 || compressLevel < 10)
                {
                    int newW = (int)(percent * rawBit.Width);
                    int newH = (int)(percent * rawBit.Height);
                    newBit = new Bitmap(newW, newH);
                    graphics = Graphics.FromImage(newBit);

                    graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
                    graphics.DrawImage(rawBit, 0, 0, newW, newH);
                    graphics.Dispose();
                    rawBit.Dispose();
                    newBit.Save(saveMemoryStream, jpegEncoder, jpeParameters);
                    newBit.Dispose();
                }
                else
                {
                    //Level为10则直接储存JPG
                    rawBit.Save(saveMemoryStream, jpegEncoder, jpeParameters);
                }
                int length = (int)saveMemoryStream.Length;
                saveMemoryStream.Position = 0;
                byte[] savedBytes = new byte[length];
                saveMemoryStream.Read(savedBytes, 0, length);
                saveMemoryStream.Close();
                saveMemoryStream.Dispose();
                return savedBytes;
            }
            catch (Exception ex)
            {
                errorMessage = Localize.Get("Save file error at ")+$"{ex.Message}";
                return null;
            }
            finally
            {
                memoryStream?.Close();
                memoryStream?.Dispose();
                saveMemoryStream?.Close();
                saveMemoryStream?.Dispose();
                rawBit?.Dispose();
                newBit?.Dispose();
                graphics?.Dispose();
            }
        }

        #endregion



        #region CompressBitmap
        /// <summary>
        /// 储存Bitmap到JPG文件。
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="savePath"></param>
        /// <param name="savedSizeInKB"></param>
        public bool SaveCompressBitmap(Bitmap bitmap, FileInfo saveFile, out long savedSizeInKB, out string errorMessage)
        {

            savedSizeInKB = 0;
            errorMessage = null;
            if (saveFile.Exists)
            {
                if (saveFile.IsReadOnly)
                {
                    errorMessage = Localize.Get("Save file is ReadOnly. ");
                    return false;
                }
            }
            else
            {
                DirectoryInfo directory = saveFile.Directory;
                if (!directory.Exists)
                {
                    try
                    {
                        directory.Create();
                    }
                    catch
                    {
                        errorMessage = Localize.Get("Cannot create save folder. ");
                        return false;
                    }
                }
            }

            FileStream stream = null;
            try
            {
                stream = saveFile.Open(FileMode.Create, FileAccess.Write, FileShare.None);
                
                bitmap.Save(stream, jpegEncoder, jpeParameters);
                savedSizeInKB = stream.Length / 1024;
                
                return true;
            }
            catch (Exception ex)
            {
                if (ex is IOException)
                {
                    errorMessage = Localize.Get("Save file is occupied. ");
                    return false;
                }
                errorMessage = Localize.Get("Save file error at ") + $"{ex.Message}";
                return false;
            }
            finally
            {
                stream?.Close();
                stream?.Dispose();
            }
        }

        /// <summary>
        /// 打开一个Image文件
        /// </summary>
        /// <param name="readFile"></param>
        /// <param name="image"></param>
        /// <param name="readSizeInKB"></param>
        /// <param name="errorMessage"></param>
        /// <returns></returns>
        public bool ReadFileToImage(FileInfo readFile, out Bitmap image, out long readSizeInKB, out string errorMessage)
        {
            image = null;
            readSizeInKB = 0;
            errorMessage = null;

            if (!readFile.Exists)
            {
                errorMessage = Localize.Get("Read file not exist. ");
                return false;
            }
            FileStream stream = null;
            try
            {
                stream = readFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
                
                Image im = Image.FromStream(stream);
                image = new Bitmap(im);
                im.Dispose();
                readSizeInKB = stream.Length / 1024;

                
                return true;

            }
            catch (Exception ex)
            {
                readSizeInKB = readFile.Length / 1024;
                errorMessage = Localize.Get("Save file error at ") + $"{ex.Message}";
                return false;
            }
            finally
            {
                stream?.Close();
                stream?.Dispose();
            }
        }


        /// <summary>
        /// 将图片压缩，不释放Image
        /// </summary>
        /// <param name="raw"></param>
        /// <returns></returns>
        public Bitmap CompressImgToBitmap(Bitmap raw)
        {

            double percentW = (double)MaxWidth / raw.Width;
            double percentH = (double)MaxHeight / raw.Height;
            double percent = Math.Min(percentW, percentH);
            percent = Math.Min(1, percent);
            if (percent < 1 || compressLevel < 10)
            {
                int newW = (int)(percent * raw.Width);
                int newH = (int)(percent * raw.Height);
                Bitmap newBit = new Bitmap(newW, newH);
                using (Graphics g = Graphics.FromImage(newBit))
                {

                    //g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
                    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
                    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
                    g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
                    g.DrawImage(raw, 0, 0, newW, newH);
                    return newBit;
                }
            }
            else
            {
                Bitmap bitmap = new Bitmap(raw);
                return bitmap;
            }

        } 
        #endregion
        bool SetJpegCompressor()
        {
            var temp = ImageCodecInfo.GetImageDecoders();
            jpegEncoder = temp.FirstOrDefault(x => x.MimeType == "image/jpeg");
            if (jpegEncoder == null) return false;

            System.Drawing.Imaging.Encoder encoder = System.Drawing.Imaging.Encoder.Quality;
            jpeParameters = new EncoderParameters(1);
            EncoderParameter parameter = new EncoderParameter(encoder, Quality);
            jpeParameters.Param[0] = parameter;
            return true;
        }
    }
}
